package RKUjava.math;
import RKUjava.datastructures.RKUVector;
import RKUjava.util.RKUStringUtils;
import java.util.Enumeration;

/** Implements a mathematical function. This class implements parsing and
    evalation of simple mathematical functions build by subfunctions and
    constants. Currently it supports the following standard constants,
    operators and functions.
    <table border=0>
    <tr><td>
    <ul>
    <li>Any constant like 0.5, 42, 90001, 6.65E5 and also pi and e.
    <li>Variables: x0, x5, x12
    <li>Parenthesises like ( )
    <li>The following binary operators: +, -, *, /, % (modulo)
    <li>Geometrical functions: sin(a), cos(a), tan(a), arcsin(a), arccos(a),
    arctan(a)<br>
    Argument must be in parenthesis, e.g. sin(5) and all arguments given are 
    in radians.
    <li>Standard math functions: pow(a,b) (= a to the power of b), sqrt(a),
    exp(a), ln(a) (= inverse of exp), log(a) (= log with base 10), 
    log(a,b) (= log of a with base b), abs(a) (= positive value of a)
    <li>Other functions: rnd() (= a random value between 0 and 1), rnd(a,b) 
    (= a random value between a and b), max(a,b) (= a if a>b else b), 
    min(a,b) (= a if a<b else b)
    </ul>
    </td></tr></table>
    <b>Note:</b> This is an extremely slow function parser but provides fast 
    evaluation. In some of the functions (pow(a,b), log(a,b)) the 
    character ',' is used to separate the arguments. ':' or ';' can be 
    used instead of ','.
*/
public class RKUFunction
{
  
  private RKUFunctionExpression func;
  private String error = "";
  
  /** Create a RKUFunction representing the expression. */
  public RKUFunction(String expression)
    {
      // This is probably the most ugly and slow functionparser ever seen!

      super();
      int start, stop, i, j, parenscnt;
      String curstr="", curstr2="";
      RKUVector tokens;
      int nummatch;
      RKUFunctionToken lasttoken = null;
      Double avalue;
      Integer anintvalue;
      int oldstart, oldstop;
      boolean lastwasoperator, updated, subupdated, parseerror, done;
      RKUFunctionToken dummy, curtok, curtok2;
      RKUFunctionToken[] near;

      start = 0;
      stop = 1;

      tokens = new RKUVector();

      expression = expression.toUpperCase();
      expression = RKUStringUtils.replace(expression," ","");
      expression = RKUStringUtils.replace(expression,"\n","");
      expression = RKUStringUtils.replace(expression,";",",");
      expression = RKUStringUtils.replace(expression,":",",");

      lastwasoperator = false;

      while(stop<=expression.length()) {
	curstr = (expression.substring(start,stop)).trim();
	nummatch = 0;

	  {
	    try {
	      if (curstr.equals("-") && stop<expression.length()) {
		avalue = Double.valueOf((expression.substring(start,stop+1)).trim());
		stop++;
	      }
	      else
		avalue = Double.valueOf(curstr);
	    
	      try {
		while(stop<=expression.length()) {
		  curstr2 = (expression.substring(start,stop)).trim();
		  if (curstr2.endsWith("E"))
		    stop++;
		  else {
		    avalue = Double.valueOf(curstr2);
		    lasttoken = new RKUFunctionToken(curstr2, 0, 24, avalue.doubleValue());
		    stop++;
		  }
		}
		// Found a number.
		start = stop - 1;
		stop--;
		nummatch++;
	      
	      }
	      catch(Exception e1) {
		// Found a number.
		start = stop - 1;
		stop--;
		nummatch++;
	      }
	    }
	    catch(Exception ee) {
	      // If not a number then just skip it.
	    };
	  }
	
	  // PARSE variables
	  if (curstr.equals("X")) {
	    oldstart = start;
	    oldstop = stop;
	    start++;
	    stop++;
	  
	    curstr2 = (expression.substring(start,stop)).trim();
	    try {
	      anintvalue = Integer.valueOf(curstr2);
	    
	      try {
		while(stop<=expression.length()) {
		  curstr2 = (expression.substring(start,stop)).trim();
		  anintvalue = Integer.valueOf(curstr2);
		  lasttoken = new RKUFunctionToken(curstr2, 0, 25, anintvalue.intValue());
		  stop++;
		}
		// Found a number.
		start = stop - 1;
		stop--;
		nummatch++;
	      }
	      catch(Exception e1) {
		// Found a number.
		start = stop - 1;
		stop--;
		nummatch++;
	      }
	    }
	    catch(Exception ee) {
	      error = error+"No variable index after x at pos "+start+".  Example: x4\n";
	      start = oldstart;
	      stop = oldstop;
	    };
	  }
	
	  if (curstr.equals("-")) {
	    if (nummatch==0) {
	      lasttoken = new RKUFunctionToken(curstr, 2, 2);
	      nummatch++;
	      lastwasoperator = true;
	    }
	  }
	  else
	    lastwasoperator = false;

	  if (curstr.equals("+")) {
	    lasttoken = new RKUFunctionToken(curstr, 2, 1);
	    nummatch++;
	    lastwasoperator = true;
	  }

	  if (curstr.equals("*")) {
	    lasttoken = new RKUFunctionToken(curstr, 2, 3);
	    nummatch++;
	    lastwasoperator = true;
	  }

	  if (curstr.equals("/")) {
	    lasttoken = new RKUFunctionToken(curstr, 2, 4);
	    nummatch++;
	    lastwasoperator = true;
	  }

	  if (curstr.equals("%")) {
	    lasttoken = new RKUFunctionToken(curstr, 2, 5);
	    nummatch++;
	    lastwasoperator = true;
	  }

	  if (curstr.equals("&")) {
	    lasttoken = new RKUFunctionToken(curstr, 2, 6);
	    nummatch++;
	    lastwasoperator = true;
	  }

	  if (curstr.equals("|")) {
	    lasttoken = new RKUFunctionToken(curstr, 2, 7);
	    nummatch++;
	    lastwasoperator = true;
	  }

	  if (curstr.equals("^")) {
	    lasttoken = new RKUFunctionToken(curstr, 2, 8);
	    nummatch++;
	    lastwasoperator = true;
	  }

	  if (curstr.equals("(")) {
	    lasttoken = new RKUFunctionToken(curstr, 0, 9);
	    nummatch++;
	  }

	  if (curstr.equals(")")) {
	    lasttoken = new RKUFunctionToken(curstr, 0, 10);
	    nummatch++;
	  }

	  if (curstr.equals("SIN")) {
	    lasttoken = new RKUFunctionToken(curstr, 4, 11);
	    nummatch++;
	  }

	  if (curstr.equals("COS")) {
	    lasttoken = new RKUFunctionToken(curstr, 4, 12);
	    nummatch++;
	  }

	  if (curstr.equals("TAN")) {
	    lasttoken = new RKUFunctionToken(curstr, 4, 13);
	    nummatch++;
	  }

	  if (curstr.equals("ARCSIN")) {
	    lasttoken = new RKUFunctionToken(curstr, 4, 14);
	    nummatch++;
	  }

	  if (curstr.equals("ARCCOS")) {
	    lasttoken = new RKUFunctionToken(curstr, 4, 15);
	    nummatch++;
	  }

	  if (curstr.equals("ARCTAN")) {
	    lasttoken = new RKUFunctionToken(curstr, 4, 16);
	    nummatch++;
	  }

	  if (curstr.equals("POW")) {
	    lasttoken = new RKUFunctionToken(curstr, 5, 17);
	    nummatch++;
	  }

	  if (curstr.equals("SQRT")) {
	    lasttoken = new RKUFunctionToken(curstr, 4, 18);
	    nummatch++;
	  }

	  if (curstr.equals("EXP")) {
	    lasttoken = new RKUFunctionToken(curstr, 5, 19);
	    nummatch++;
	  }
	  else if (((expression.substring(start,Math.min(start+3, expression.length()))).trim()).equals("EXP")) {
	    nummatch +=2;
	  }

	  if (curstr.equals("LN")) {
	    lasttoken = new RKUFunctionToken(curstr, 4, 20);
	    nummatch++;
	  }

	  if (curstr.equals("LOG")) {
	    lasttoken = new RKUFunctionToken(curstr, 7, 21);
	    nummatch++;
	  }

	  if (curstr.equals("RND")) {
	    lasttoken = new RKUFunctionToken(curstr, 6, 22);
	    nummatch++;
	  }

	  if (curstr.equals(",")) {
	    lasttoken = new RKUFunctionToken(curstr, 0, 23);
	    nummatch++;
	  }

	  if (curstr.equals("MAX")) {
	    lasttoken = new RKUFunctionToken(curstr, 6, 26);
	    nummatch++;
	  }

	  if (curstr.equals("MIN")) {
	    lasttoken = new RKUFunctionToken(curstr, 6, 27);
	    nummatch++;
	  }

	  if (curstr.equals("ABS")) {
	    lasttoken = new RKUFunctionToken(curstr, 4, 28);
	    nummatch++;
	  }

	  if (curstr.equals("PI") && nummatch==0) {
	    lasttoken = new RKUFunctionToken(curstr, 0, 24, Math.PI);
	    nummatch++;
	  }

	  if (curstr.equals("E") && nummatch==0) {
	    lasttoken = new RKUFunctionToken(curstr, 0, 24, Math.E);
	    nummatch++;
	  }


	  if (nummatch==1) {
	    tokens.addElement(lasttoken);
	    start = stop;
	  }

	  stop++;
      }

      if (start!=stop-1) {
	error = error + "Couldn't parse end of function definition: "+curstr+"\n";
      };

      if (error.length()>0) {
	throw new RKUParseErrorException("Parse error in RKUFunction: "+expression+"\nError: \n"+error);
      }

      // **** Insert extra parenthesises *********************************

      i=0;
      while (i<tokens.size()) {
	curtok = (RKUFunctionToken)tokens.elementAt(i);
	    
	if(curtok.subtype==3 || curtok.subtype==4 || curtok.subtype==5) {
		
	  parenscnt = 0;
	  done = false;
	  j = i-1;
	  while (j>=0 && !done) {
	    curtok2 = (RKUFunctionToken)tokens.elementAt(j);
	    if (curtok2.subtype==10)
	      parenscnt++;

	    if (curtok2.subtype==9) {
	      parenscnt--;
			
	      if (j>0) {
		curtok2 = (RKUFunctionToken)tokens.elementAt(j-1);
		if ((curtok2.subtype>=11 && curtok2.subtype<=22) ||
		    (curtok2.subtype>=26 && curtok2.subtype<=28)) {
		  j--;
		}
	      }
	    }

	    if (parenscnt == 0) {
	      tokens.insertElementAt(new RKUFunctionToken("(",0,9),j);
	      done = true;
	      i++;
	    }
	    else {
	      j--;
	    }
	  }

	  parenscnt = 0;
	  done = false;
	  j = i+1;
	  while (j<tokens.size() && !done) {
	    curtok2 = (RKUFunctionToken)tokens.elementAt(j);
		    
	    if ((curtok2.subtype>=11 && curtok2.subtype<=22) ||
		(curtok2.subtype>=26 && curtok2.subtype<=28)) {
	      j++;
	      curtok2 = (RKUFunctionToken)tokens.elementAt(j);
	    }

	    if (curtok2.subtype==9)
	      parenscnt++;

	    if (curtok2.subtype==10) {
	      parenscnt--;
	    }

	    if (parenscnt == 0) {
	      tokens.insertElementAt(new RKUFunctionToken(")",0,10),j+1);
	      done = true;
	    }
	    else {
	      j++;
	    }
	  }
	}
	i++;
      };

      // **** Parse the tokens! ******************************************	
      parseerror = false;
      dummy = new RKUFunctionToken("", -1,-1);
      near = new RKUFunctionToken[7];

      while (!parseerror && tokens.size()!=1) {
	updated = false;

	// Scan for parenthesis and functions.
	subupdated = true;

	while (subupdated) {
	  subupdated = false;
	  i = 0;
	  while (i<tokens.size()) {
	    near[0] = (RKUFunctionToken)tokens.elementAt(i);
		    
	    if (i<tokens.size()-1) 
	      near[1] = (RKUFunctionToken)tokens.elementAt(i+1);
	    else
	      near[1] = dummy;
		    
	    if (i<tokens.size()-2) 
	      near[2] = (RKUFunctionToken)tokens.elementAt(i+2);
	    else
	      near[2] = dummy;
		    
	    if (i<tokens.size()-3) 
	      near[3] = (RKUFunctionToken)tokens.elementAt(i+3);
	    else
	      near[3] = dummy;
		    
	    if (i<tokens.size()-4) 
	      near[4] = (RKUFunctionToken)tokens.elementAt(i+4);
	    else
	      near[4] = dummy;

	    if (i<tokens.size()-5) 
	      near[5] = (RKUFunctionToken)tokens.elementAt(i+5);
	    else
	      near[5] = dummy;

	    if (i<tokens.size()-6) 
	      near[6] = (RKUFunctionToken)tokens.elementAt(i+6);
	    else
	      near[6] = dummy;
		    
	    /* Functions with zero args: rnd */
	    if ((near[0].subtype==22) &&
		near[1].subtype==9 && near[2].subtype == 10) {

	      near[0].isparsed = true;
	      tokens.removeElementAt(i+1);
	      tokens.removeElementAt(i+1);
	      subupdated = true;
	      updated = true;
	      i--;
	    }
	    /* Functions with one arg:
	       Sin, cos, tan, arcsin, arccos, arctan, sqrt, exp,
	       ln, log(a) */
	    else if (((near[0].subtype>=11 && near[0].subtype<=21 &&
		       near[0].subtype!=17) ||
		      near[0].subtype==28) && near[1].subtype==9 &&
		     near[2].isparsed && near[3].subtype == 10) {

	      near[0].isparsed = true;
	      near[0].func.setSon(near[2].func);
	      tokens.removeElementAt(i+1);
	      tokens.removeElementAt(i+1);
	      tokens.removeElementAt(i+1);
	      subupdated = true;
	      updated = true;
	      i--;
	    }
	    /* functions with two args: pow, log, rnd */
	    else if ((near[0].subtype==17 || near[0].subtype==21
		      || near[0].subtype==22 || near[0].subtype==26
		      || near[0].subtype==27) &&
		     near[1].subtype==9 && near[2].isparsed && 
		     near[3].subtype == 23 && near[4].isparsed &&
		     near[5].subtype == 10) {
	      near[0].isparsed = true;
	      near[0].func.setSons(near[2].func, near[4].func);
	      tokens.removeElementAt(i+1);
	      tokens.removeElementAt(i+1);
	      tokens.removeElementAt(i+1);
	      tokens.removeElementAt(i+1);
	      tokens.removeElementAt(i+1);
	      subupdated = true;
	      updated = true;
	      i--;
	    }
	    // Parenthesis
	    else if (near[0].subtype==9 &&
		     near[1].isparsed && near[2].subtype == 10) {
	      tokens.removeElementAt(i);
	      tokens.removeElementAt(i+1);
	      subupdated = true;
	      updated = true;
	      i--;
	    }
	    i++;
	  }
	}

	    
	// Scan for high precedence operators * / % 
	subupdated = true;

	while (subupdated) {
	  subupdated = false;
	  i = 0;
	  while (i<tokens.size()) {
	    near[0] = (RKUFunctionToken)tokens.elementAt(i);
		    
	    if (i<tokens.size()-1) 
	      near[1] = (RKUFunctionToken)tokens.elementAt(i+1);
	    else
	      near[1] = dummy;
		    
	    if (i<tokens.size()-2) 
	      near[2] = (RKUFunctionToken)tokens.elementAt(i+2);
	    else
	      near[2] = dummy;
		    
	    if (near[0].isparsed && near[2].isparsed && 
		(near[1].subtype == 3 || near[1].subtype == 4 || near[1].subtype == 5)) {
	      near[1].func.setSons(near[0].func, near[2].func);
	      near[1].isparsed = true;
	      tokens.removeElementAt(i);
	      tokens.removeElementAt(i+1);
	      subupdated = true;
	      updated = true;
	      i--;
	    }
	    i++;
	  }
	}
			
	// Scan for medium precedence operators + -
	subupdated = true;

	while (subupdated) {
	  subupdated = false;
	  i = 0;
	  while (i<tokens.size()) {
	    near[0] = (RKUFunctionToken)tokens.elementAt(i);
		    
	    if (i<tokens.size()-1) 
	      near[1] = (RKUFunctionToken)tokens.elementAt(i+1);
	    else
	      near[1] = dummy;
		    
	    if (i<tokens.size()-2) 
	      near[2] = (RKUFunctionToken)tokens.elementAt(i+2);
	    else
	      near[2] = dummy;
		    
	    if (near[0].isparsed && near[2].isparsed && 
		(near[1].subtype == 1 || near[1].subtype == 2)) {
	      near[1].func.setSons(near[0].func, near[2].func);
	      near[1].isparsed = true;
	      tokens.removeElementAt(i);
	      tokens.removeElementAt(i+1);
	      subupdated = true;
	      updated = true;
	      i--;
	    }
	    // Unary minus
	    else if ((near[0].subtype>=1 && near[0].subtype<=9 && 
		      !near[0].isparsed) && 
		     near[1].subtype == 2 && near[2].isparsed) {

	      near[1].func.setSons(new RKUFConstant(0),near[2].func);
	      near[1].isparsed = true;
	      tokens.removeElementAt(i+2);
	      subupdated = true;
	      updated = true;
	      i--;
	    }
	    // Unary minus
	    else if (near[0].subtype==2 && i==0 && near[1].isparsed) {
	      near[0].func.setSons(new RKUFConstant(0),near[1].func);
	      near[0].isparsed = true;
	      tokens.removeElementAt(i+1);
	      subupdated = true;
	      updated = true;
	      i--;
	    };

	    i++;
	  }
	}

	if (!updated && tokens.size()!=1)
	  parseerror = true;

      }

      if (parseerror)
	throw new RKUParseErrorException("\nParse error in RKUFunction: "+expression+"\nError: \n"+error);

      func = ((RKUFunctionToken)tokens.elementAt(0)).func;
    }

  /** Evaluate the function. You should only call this method if you are
      sure that the function doesn't contain any variables. */
  public double evaluate()
    {
      return func.evaluate();
    }

  /** Evaluate the function. 
      @param vars An array containing the variables for the function.
  */
  public double evaluate(double[] vars)
    {
      return func.evaluate(vars);
    }

  /** Test whether the given string is can be parsed. If you use this method
      and in an if-sentence and then build the function it will take twice
      the time to just build it.
  */       
  public boolean isValid(String expression)
    {
      RKUFunction tst;
      try {
	tst = new RKUFunction(expression);
	return true;
      }
      catch(Exception ee) { return false;}
    }

  /** Return a string representing the function. */
  public String toString()
    {
      return func.toString();
    };
    
}

class RKUFunctionToken
{
  String txt;

  /*
    0 = terminal
    1 = unary operator: -
    2 = binary operator:  + - * / % & | ^
    3 = func with zero arguments: 
    4 = func with one argument:  sin(a), exp(a)
    5 = func with two arguments: pow(a,b)
    6 = func with zero or two arguments: rnd() or rnd(a,b)
    7 = func with one or two arguments: log(a) or log(a,b)
  */
  int type;

  /*
    0  = - (unary)
    1  = +
    2  = - (binary)
    3  = *
    4  = /
    5  = %
    6  = &
    7  = |
    8  = ^
    9  = (
    10 = )
    11 = sin
    12 = cos
    13 = tan
    14 = arcsin
    15 = arccos
    16 = arctan
    17 = pow
    18 = sqrt
    19 = exp
    20 = ln
    21 = log
    22 = rnd
    23 = ,
    24 = constant
    25 = variable
    26 = max
    27 = min
    28 = abs
  */
  int subtype;

  double value;
  int index;

  RKUFunctionExpression func;
  boolean isparsed;

  public RKUFunctionToken(String atxt, int atype)
    {
      super();

      txt = atxt;
      type = atype;
      subtype = 0;
      setrest();
    }

  public RKUFunctionToken(String atxt, int atype, int asubtype)
    {
      super();

      txt = atxt;
      type = atype;
      subtype = asubtype;
      setrest();
    }

  public RKUFunctionToken(String atxt, int atype, int asubtype, double avalue)
    {
      super();

      txt = atxt;
      type = atype;
      subtype = asubtype;
      value = avalue;
      setrest();
    }

  public RKUFunctionToken(String atxt, int atype, int asubtype, int anindex)
    {
      super();

      txt = atxt;
      type = atype;
      subtype = asubtype;
      index = anindex;
      setrest();
    }

  private void setrest()
    {
      switch(subtype) {
      case 0:
	
	isparsed = false;
	break;
      case 1:
	func = new RKUFPlus();
	isparsed = false;
	break;
      case 2:
	func = new RKUFMinus();
	isparsed = false;
	break;
      case 3:
	func = new RKUFMult();
	isparsed = false;
	break;
      case 4:
	func = new RKUFDiv();
	isparsed = false;
	break;
      case 5:
	func = new RKUFMod();
	isparsed = false;
	break;
      case 11:
	func = new RKUFSin();
	isparsed = false;
	break;
      case 12:
	func = new RKUFCos();
	isparsed = false;
	break;
      case 13:
	func = new RKUFTan();
	isparsed = false;
	break;
      case 14:
	func = new RKUFArcsin();
	isparsed = false;
	break;
      case 15:
	func = new RKUFArccos();
	isparsed = false;
	break;
      case 16:
	func = new RKUFArctan();
	isparsed = false;
	break;
      case 17:
	func = new RKUFPow();
	isparsed = false;
	break;
      case 18:
	func = new RKUFSqrt();
	isparsed = false;
	break;
      case 19:
	func = new RKUFExp();
	isparsed = false;
	break;
      case 20:
	func = new RKUFLn();
	isparsed = false;
	break;
      case 21:
	func = new RKUFLog();
	isparsed = false;
	break;
      case 22:
	func = new RKUFRnd();
	isparsed = false;
	break;
      case 24:
	func = new RKUFConstant(value);
	isparsed = true;
	break;
      case 25:
	func = new RKUFVariable(index);
	isparsed = true;
	break;
      case 26:
	func = new RKUFMax();
	isparsed = false;
	break;
      case 27:
	func = new RKUFMin();
	isparsed = false;
	break;
      case 28:
	func = new RKUFAbs();
	isparsed = false;
	break;
      }
    }
}

class RKUFunctionExpression
{
  RKUFunctionExpression[] sons;

  public RKUFunctionExpression(RKUFunctionExpression[] somesons)
    {
      super();
      sons = somesons;
    }

  public RKUFunctionExpression()
    {
      super();
      sons = new RKUFunctionExpression[0];
    }

  public void setSons(RKUFunctionExpression[] somesons)
    {
      sons = somesons;
    }

  public void setSon(RKUFunctionExpression ason)
    {
      if (sons.length == 0)
	sons = new RKUFunctionExpression[1];
      sons[0] = ason;
    }

  public void setSons(RKUFunctionExpression son1, RKUFunctionExpression son2)
    {
      if (sons.length == 0)
	sons = new RKUFunctionExpression[2];
      sons[0] = son1;
      sons[1] = son2;
    }

  public double evaluate()
    {
      return 0;
    }

  public double evaluate(double[] vars)
    {
      return 0;
    }
}

class RKUFConstant extends RKUFunctionExpression
{
  double value;

  public RKUFConstant(double avalue)
    {
      super();
      value = avalue;
    }

  public double evaluate() { return value; };
  public double evaluate(double[] vars) { return value; };
  public String toString() { return ""+value; };
}

class RKUFVariable extends RKUFunctionExpression
{
  int index;

  public RKUFVariable(int anindex)
    {
      super();
      index = anindex;
    }

  public double evaluate() { return 0; };
  public double evaluate(double[] vars) { return vars[index]; };
  public String toString() { return "x"+index; };
}

class RKUFPlus extends RKUFunctionExpression
{
  public RKUFPlus() { super(); }
  public double evaluate() { return sons[0].evaluate()+sons[1].evaluate(); };
  public double evaluate(double[] vars) { return sons[0].evaluate(vars)+sons[1].evaluate(vars); };
  public String toString() { return "("+sons[0].toString()+"+"+sons[1].toString()+")"; };
};

class RKUFMinus extends RKUFunctionExpression
{
  public RKUFMinus() { super(); }
  public double evaluate() { return sons[0].evaluate()-sons[1].evaluate(); };
  public double evaluate(double[] vars) { return sons[0].evaluate(vars)-sons[1].evaluate(vars); };
  public String toString() 
    { 
      String first = sons[0].toString();
      if (first.equals("0.0"))
	return "-"+sons[1].toString();
      else
	return "("+sons[0].toString()+"-"+sons[1].toString()+")"; 
    };
}

class RKUFMult extends RKUFunctionExpression
{
  public RKUFMult() { super(); }
  public double evaluate() { return sons[0].evaluate()*sons[1].evaluate(); };
  public double evaluate(double[] vars) { return sons[0].evaluate(vars)*sons[1].evaluate(vars); };
  public String toString() { return "("+sons[0].toString()+"*"+sons[1].toString()+")"; };
}

class RKUFDiv extends RKUFunctionExpression
{
  public RKUFDiv() { super(); }
  public double evaluate() { return sons[0].evaluate()/sons[1].evaluate(); };
  public double evaluate(double[] vars) { return sons[0].evaluate(vars)/sons[1].evaluate(vars); };
  public String toString() { return "("+sons[0].toString()+"/"+sons[1].toString()+")"; };
}


class RKUFMod extends RKUFunctionExpression
{
  public RKUFMod() { super(); }
  public double evaluate() { return sons[0].evaluate()%sons[1].evaluate(); };
  public double evaluate(double[] vars) { return sons[0].evaluate(vars)%sons[1].evaluate(vars); };
  public String toString() { return "("+sons[0].toString()+"%"+sons[1].toString()+")"; };
}

class RKUFSin extends RKUFunctionExpression
{
  public RKUFSin() { super(); }
  public double evaluate() { return Math.sin(sons[0].evaluate()); };
  public double evaluate(double[] vars) { return Math.sin(sons[0].evaluate(vars)); };
  public String toString() { return "sin("+sons[0].toString()+")"; };
}

class RKUFCos extends RKUFunctionExpression
{
  public RKUFCos() { super(); }
  public double evaluate() { return Math.cos(sons[0].evaluate()); };
  public double evaluate(double[] vars) { return Math.cos(sons[0].evaluate(vars)); };
  public String toString() { return "cos("+sons[0].toString()+")"; };
}

class RKUFTan extends RKUFunctionExpression
{
  public RKUFTan() { super(); }
  public double evaluate() { return Math.tan(sons[0].evaluate()); };
  public double evaluate(double[] vars) { return Math.tan(sons[0].evaluate(vars)); };
  public String toString() { return "tan("+sons[0].toString()+")"; };
}

class RKUFArcsin extends RKUFunctionExpression
{
  public RKUFArcsin() { super(); }
  public double evaluate() { return Math.asin(sons[0].evaluate()); };
  public double evaluate(double[] vars) { return Math.asin(sons[0].evaluate(vars)); };
  public String toString() { return "arcsin("+sons[0].toString()+")"; };
}

class RKUFArccos extends RKUFunctionExpression
{
  public RKUFArccos() { super(); }
  public double evaluate() { return Math.acos(sons[0].evaluate()); };
  public double evaluate(double[] vars) { return Math.acos(sons[0].evaluate(vars)); };
  public String toString() { return "arccos("+sons[0].toString()+")"; };
}

class RKUFArctan extends RKUFunctionExpression
{
  public RKUFArctan() { super(); }
  public double evaluate() { return Math.atan(sons[0].evaluate()); };
  public double evaluate(double[] vars) { return Math.atan(sons[0].evaluate(vars)); };
  public String toString() { return "arctan("+sons[0].toString()+")"; };
}

class RKUFSqrt extends RKUFunctionExpression
{
  public RKUFSqrt() { super(); }
  public double evaluate() { return Math.sqrt(sons[0].evaluate()); };
  public double evaluate(double[] vars) { return Math.sqrt(sons[0].evaluate(vars)); };
  public String toString() { return "sqrt("+sons[0].toString()+")"; };
}

class RKUFExp extends RKUFunctionExpression
{
  public RKUFExp() { super(); }
  public double evaluate() { return Math.exp(sons[0].evaluate()); };
  public double evaluate(double[] vars) { return Math.exp(sons[0].evaluate(vars)); };
  public String toString() { return "exp("+sons[0].toString()+")"; };
}

class RKUFLn extends RKUFunctionExpression
{
  public RKUFLn() { super(); }
  public double evaluate() { return Math.log(sons[0].evaluate()); };
  public double evaluate(double[] vars) { return Math.log(sons[0].evaluate(vars)); };
  public String toString() { return "ln("+sons[0].toString()+")"; };
}

class RKUFLog extends RKUFunctionExpression
{
  public RKUFLog() 
    {
      super(); 
      sons = new RKUFunctionExpression[2];
      sons[1] = new RKUFConstant(10);
    }
  public double evaluate() { return Math.log(sons[0].evaluate())/Math.log(sons[1].evaluate()); };
  public double evaluate(double[] vars) { return Math.log(sons[0].evaluate(vars))/Math.log(sons[1].evaluate()); };
  public String toString() { return "log("+sons[0].toString()+","+sons[1].toString()+")"; };
}

class RKUFPow extends RKUFunctionExpression
{
  public RKUFPow() { super(); }
  public double evaluate() { return Math.pow(sons[0].evaluate(), sons[1].evaluate()); };
  public double evaluate(double[] vars) { return Math.pow(sons[0].evaluate(vars),sons[1].evaluate(vars)); };
  public String toString() { return "pow("+sons[0].toString()+","+sons[1].toString()+")"; };
}

class RKUFRnd extends RKUFunctionExpression
{
  public RKUFRnd() 
    { 
      super();
      sons = new RKUFunctionExpression[2];
      sons[0] = new RKUFConstant(0);
      sons[1] = new RKUFConstant(1);
    }
  public double evaluate() 
    {
      double low, high;
      low = sons[0].evaluate();
      high = sons[1].evaluate();
      return low + Math.random()*(high-low);
    }
  public double evaluate(double[] vars) 
    {
      double low, high;
      low = sons[0].evaluate(vars);
      high = sons[1].evaluate(vars);
      return low + Math.random()*(high-low);
    }

  public String toString() { return "rnd("+sons[0].toString()+","+sons[1].toString()+")"; };
}

class RKUFMax extends RKUFunctionExpression
{
  public RKUFMax() { super(); }
  public double evaluate() { return Math.max(sons[0].evaluate(),sons[1].evaluate()); };
  public double evaluate(double[] vars) { return Math.max(sons[0].evaluate(vars),sons[1].evaluate(vars)); };
  public String toString() { return "max("+sons[0].toString()+","+sons[1].toString()+")"; };
}

class RKUFMin extends RKUFunctionExpression
{
  public RKUFMin() { super(); }
  public double evaluate() { return Math.min(sons[0].evaluate(),sons[1].evaluate()); };
  public double evaluate(double[] vars) { return Math.min(sons[0].evaluate(vars),sons[1].evaluate(vars)); };
  public String toString() { return "min("+sons[0].toString()+","+sons[1].toString()+")"; };
}

class RKUFAbs extends RKUFunctionExpression
{
  public RKUFAbs() { super(); }
  public double evaluate() { return Math.abs(sons[0].evaluate()); };
  public double evaluate(double[] vars) { return Math.abs(sons[0].evaluate(vars)); };
  public String toString() { return "abs("+sons[0].toString()+")"; };
}

